#include "ngx_mem_pool.h"

#include <cstdlib>
#include <cstring>
bool ngx_mem_pool::ngx_create_pool(size_t size) {
    _pool = (ngx_pool_t *)malloc(size);

    if (!_pool) return false;  // 内存开辟失败

    _pool->d.last = (unsigned char *)_pool + sizeof(ngx_pool_t);
    _pool->d.end = (unsigned char *)_pool + size;
    _pool->d.next = nullptr;
    _pool->d.failed = 0;

    size -= sizeof(ngx_pool_t);
    _pool->max =
        (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    _pool->current = _pool;
    _pool->large = nullptr;
    _pool->cleanup = nullptr;

    return true;
}

void *ngx_mem_pool::ngx_palloc(size_t size) {
    if (size <= _pool->max) {
        return ngx_palloc_small(size, 1);
    }
    return ngx_palloc_large(size);
}

void *ngx_mem_pool::ngx_pnalloc(size_t size) {
    if (size <= _pool->max) {
        return ngx_palloc_small(size, 0);
    }
    return ngx_palloc_large(size);
}

void *ngx_mem_pool::ngx_pcalloc(size_t size) {
    void *p;
    p = ngx_palloc(size);
    if (p) {
        ngx_memzero(p, size);
    }
    return p;
}

void *ngx_mem_pool::ngx_palloc_small(size_t size, unsigned int align) {
    unsigned char *m;
    ngx_pool_t *p = _pool->current;

    do {
        m = p->d.last;
        if (align) {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

        if ((size_t)(p->d.end - m) >= size) {
            p->d.last = m + size;  // 分配内存
            return m;
        }

        p = p->d.next;
    } while (p);

    return ngx_palloc_block(size);  // 前面的内存池 都没有足够的小块内存可用
}

void *ngx_mem_pool::ngx_palloc_block(size_t size) {
    unsigned char *m;
    size_t psize;
    ngx_pool_t *p, *newpool;

    psize = (size_t)(_pool->d.end - (unsigned char *)_pool);

    m = (unsigned char *)malloc(psize);

    if (!m) {
        return nullptr;  // 开辟内存失败
    }

    newpool = (ngx_pool_t *)m;
    newpool->d.end = m + psize;
    newpool->d.next = nullptr;
    newpool->d.failed = 0;

    // 注意新的内存池头部存放的结构体不再是ngx_pool_t
    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    newpool->d.last = m + size;  // 分配出去size大小的内存

    // 将前面内存池分配失败的累计分配失败次数，达到5次之后
    // 不再从该内存池中分配内存
    for (p = _pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            _pool->current = p->d.next;
        }
    }

    p->d.next = newpool;

    return m;
}

void *ngx_mem_pool::ngx_palloc_large(size_t size) {
    void *p;
    unsigned int n;
    ngx_pool_large_t *large;

    p = malloc(size);
    if (!p) return nullptr;

    n = 0;

    for (large = _pool->large; large; large = large->next) {
        if (large->alloc == nullptr) {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) {
            // 找了4次 没有找到合适的大块内存信息头
            break;
        }
    }

    // 既然没有合适的大块内存信息头，就在小块内存池中分配空间
    large = (ngx_pool_large_t *)ngx_palloc_small(sizeof(ngx_pool_large_t), 1);
    if (!large) {
        // 如果小块内存分配失败 则说明系统内存不够 释放之前开辟的大块内存
        free(p);
        return nullptr;
    }

    large->alloc = p;
    large->next = _pool->large;
    _pool->large = large;

    return p;
}

void ngx_mem_pool::ngx_pfree(void *p) {
    ngx_pool_large_t *l;
    for (l = _pool->large; l; l = l->next) {
        if (p == l->alloc) {
            free(l->alloc);
            l->alloc = nullptr;
            return;
        }
    }
}

// 不回收小块内存，仅通过重置来回收小块内存池(短链接特性，定时释放)
void ngx_mem_pool::ngx_reset_pool() {
    ngx_pool_t *p;
    ngx_pool_large_t *l;
    for (l = _pool->large; l; l = l->next) {
        if (l->alloc) {
            free(l->alloc);
        }
    }

    // 处理第一个内存池块
    p = _pool;
    p->d.last = (unsigned char *)p + sizeof(ngx_pool_t);
    p->d.failed = 0;

    // 第二块及之后的内存池块
    for (p = p->d.next; p; p = p->d.next) {
        p->d.last = (unsigned char *)p + sizeof(ngx_pool_data_t);
        p->d.failed = 0;
    }

    _pool->current = _pool;
    _pool->large = nullptr;
}

void ngx_mem_pool::ngx_destroy_pool() {
    ngx_pool_t *p, *n;
    ngx_pool_large_t *l;
    ngx_pool_cleanup_t *c;

    for (c = _pool->cleanup; c; c = c->next) {
        if (c->handler) {
            c->handler(c->data);
        }
    }

    for (l = _pool->large; l; l = l->next) {
        if (l->alloc) {
            free(l->alloc);
        }
    }

    for (p = _pool, n = _pool->d.next;; p = n, n = n->d.next) {
        free(p);
        if (!n) {
            break;
        }
    }
}

ngx_pool_cleanup_t *ngx_mem_pool::ngx_pool_cleanup_add(size_t size) {
    ngx_pool_cleanup_t *c;
    // 在小号内存池中分配
    c = (ngx_pool_cleanup_t *)ngx_palloc(sizeof(ngx_pool_cleanup_t));
    if (!c) return nullptr;
    if (size) {
        // 回调函数有参数
        c->data = ngx_palloc(size);  // 回调函数的参数所需空间也一并用内存池分配
        if (!c->data) {
            return nullptr;
        }
    } else {
        // 回调函数无参数
        c->data = nullptr;
    }
    c->handler = nullptr;
    c->next = _pool->cleanup;
    _pool->cleanup = c;

    return c;
}
